package pr.lanmu.service;

import cn.hutool.json.JSONUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.update.UpdateChain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pr.lanmu.annotation.CustomLog;
import pr.lanmu.common.mapper.UserMapper;
import pr.lanmu.common.po.Menu;
import pr.lanmu.common.po.Module;
import pr.lanmu.common.po.User;
import pr.lanmu.config.base.UserBase;
import pr.lanmu.config.util.*;
import pr.lanmu.config.web.CustomException;
import pr.lanmu.config.web.CustomPageInfo;
import pr.lanmu.controller.request.UserReq;
import pr.lanmu.controller.response.UserRes;
import pr.lanmu.dao.UserDao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@CustomLog
@SuppressWarnings("all")
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserDao userDao;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private EncodeUtil encodeUtil;

    public Res<UserRes.login> login(UserReq.login param) {
        User UserBase = QueryChain.of(User.class).where(User::getAccount).eq(param.getAccount()).oneOpt()
                .orElseThrow(() -> new CustomException("账号错误"));
        if (Objects.isNull(UserBase)) {
            throw new CustomException("账号错误");
        } else if (!encodeUtil.bcryptCheck(param.getPassword(), UserBase.getPassword())) {
            throw new CustomException("密码错误");
        }
        UserBase.setPassword(null);
        return Res.success(B.builder(new UserRes.login())
                .set(UserRes.login::setUser, UserBase)
                .set(UserRes.login::setToken, jwtUtil.createJwtStr(UserBase.getId()))
                .build());
    }

    public Res<CustomPageInfo<UserRes.list>> list(UserReq.list param) {
        PageHelper.startPage(param.getPageIndex(), param.getPageSize());
        List<Map<String, Object>> list = userDao.list(param);
        PageInfo<?> page = new PageInfo<>(list);
        List<UserRes.list> data = list.stream()
                .map(e -> JSONUtil.parse(e)
                        .toBean(UserRes.list.class))
                .collect(Collectors.toList());
        return Res.success(PageUtil.getPageInfo(page, data));
    }

    public Res<?> add(UserReq.add param) {
        QueryChain.of(User.class)
                .where(User::getAccount).eq(param.getAccount())
                .oneOpt()
                .ifPresent((e) -> {
                    throw new CustomException("账号已存在");
                });
        User user = ConvertUtil.copy(param, new User());
        user.setPassword(encodeUtil.bcryptEncode(param.getPassword()));
        if (Objects.isNull(param.getRole())) user.setRoleId("1");
        else user.setRoleId(String.join(",", param.getRole()));
        userMapper.insertSelective(user);
        return Res.success();
    }

    public Res<?> del(UserReq.del param) {
        //查询用户是否存在
        User userBase = QueryChain.of(User.class)
                .where(User::getId).eq(param.getId())
                .oneOpt()
                .orElseThrow(() -> new CustomException("用户不存在"));
        //判断是否超管
        if (userBase.getAdmin()) throw new CustomException("超管不可删除");
        userMapper.deleteById(param.getId());
        return Res.success();
    }

    public Res<?> setRole(UserReq.setRole param) {
        UpdateChain.of(User.class)
                .set(User::getRoleId, String.join(",", param.getRole()))
                .where(User::getId).eq(param.getId())
                .update();
        return Res.success();
    }

    public Res<?> setPassword(UserReq.setPassword param) {
        //查询当前登陆人是否超管
        User userBase = Current.getUser();
        if (!userBase.getAdmin()) throw new CustomException("非超管不可修改");
        //查询是否超管
        userBase = QueryChain.of(User.class)
                .where(User::getId).eq(param.getId())
                .oneOpt()
                .orElseThrow(() -> new CustomException("用户不存在"));
        if (userBase.getAdmin()) throw new CustomException("超管不可修改");
        UpdateChain.of(User.class)
                .where(User::getId).eq(param.getId())
                .set(User::getPassword, encodeUtil.bcryptEncode(param.getPassword()))
                .update();
        return Res.success();
    }

    public Res<?> changePassword(UserReq.changePassword param) {
        User userBase = QueryChain.of(User.class)
                .where(User::getId).eq(Current.getUserId())
                .one();
        if (!encodeUtil.bcryptCheck(param.getOldPassword(), userBase.getPassword())) return Res.fail("旧密码错误");
        UpdateChain.of(User.class)
                .where(User::getId).eq(Current.getUserId())
                .set(User::getPassword, encodeUtil.bcryptEncode(param.getNewPassword()))
                .update();
        return Res.success();
    }

    public Res<List<Menu>> menu(UserReq.menu param) {
        return Res.success(userDao.menu(Current.getUserId()));
    }

    /**
     * 获取用户信息
     *
     * @param param .
     * @return .
     */
    public Res<UserBase> userInfo(UserReq.userInfo param) {
        return Res.success(Current.getUser());
    }

    /**
     * 获取登录用户模块
     *
     * @param param .
     * @return .
     */
    public Res<List<UserRes.module>> module(UserReq.module param) {
        //根据tag和pid查询模块
        String tag = param.getTag();
        Module module = QueryChain.of(Module.class)
                .where(Module::getTag).eq(tag)
                .and(Module::getPid).eq(0L)
                .oneOpt()
                .orElseThrow(() -> new CustomException("模块不存在"));
        //查询当登陆人的模块
        List<Module> modules = userDao.module(Current.getUserId(), tag);
        return Res.success(getTree(modules, module.getId()));
    }

    /**
     * 递归组合模块树
     *
     * @param list .
     * @param pid  .
     * @return .
     */
    private List<UserRes.module> getTree(List<? extends Module> list, Long pid) {
        List<UserRes.module> root = new ArrayList<>();
        list.forEach(module -> {
            if (!Objects.equals(module.getPid(), pid)) return;
            UserRes.module item = ConvertUtil.copy(module, new UserRes.module());
            root.add(item);
            List<UserRes.module> tree = getTree(list, item.getId());
            item.setChildren(tree.isEmpty() ? null : tree);
        });
        return root;
    }
}
